#include "Campo.h"

#include <cstring>

Campo::Campo():
tipo(VOID),
longitud(0),
valor(NULL)
{
}

Campo::Campo(const char *valor,const int longitud):
tipo(CHAR)
{
	// En caso de ser cero la longitud pasada, se asigna la longitud del valor
	// pasado, sino se asigna la longitud pasada.
	this->longitud=((longitud==0)?strlen(valor):longitud)+1;
	
	// Se reserva la memoria para el campo.
	this->valor=new char[this->longitud];
	
	// Se asigna el valor pasado.
	(*this)=valor;
}

Campo::Campo (const Campo &campo):
tipo(VOID),
longitud(0),
valor(NULL)
{
	*this=campo;
}

Campo::Campo(istream &is):
tipo(VOID),
longitud(0),
valor(NULL)
{
	string tipo;
	
	is >> tipo;
	
	if (tipo=="int")
		*this=(int)0;
	else if (tipo=="long")
		*this=(long)0;
	else if (tipo=="float")
		*this=(float)0;
	else if (tipo=="double")
		*this=(double)0;
	else if (tipo=="char")
	{
		int longitud;
		is >> longitud;
		Campo campo("",longitud);
		*this=campo;
	}
}


Campo::~Campo()
{
	if (this->valor!=NULL)
		delete[] (char*)this->valor;
}

Campo &Campo::operator=(const Campo &campo)
{
	if (this!=&campo)
	{
		this->tipo=campo.tipo;
		this->longitud=campo.longitud;
		if (this->valor!=NULL)
			delete[] (char*)this->valor;

		if (campo.longitud>0)
		{
			this->valor=new char[campo.longitud];
			memcpy(this->valor,campo.valor,campo.longitud);
		}
		else
			this->valor=NULL;
	}

	return *this;
}

Campo &Campo::operator=(const char *valor)
{
	// Se obtiene la longitud de la cadena pasada.
	int longitudCadena=strlen(valor);

	// En caso de no ser un campo de tipo CHAR...
	if (this->tipo!=CHAR)
	{
		// ...se lo convierte a uno de dicho tipo.

		this->tipo=CHAR;
	
		this->longitud=longitudCadena+1;
	
		if (this->valor!=NULL)
			delete[] (char*)this->valor;

		this->valor=new char[this->longitud];
	}

	// Se obtiene la longitud a copiar.
	int longitudCopiar=longitudCadena<(this->longitud-1)?
		longitudCadena:(this->longitud-1);

	// Se setea el valor.
	memcpy(this->valor,valor,longitudCopiar);

	// Se limpia la parte restante.
	memset((char *)this->valor+longitudCopiar,0,this->longitud-longitudCopiar);

	return *this;
}

Campo &Campo::operator=(const int &valor)
{
	this->tipo=INT;
	this->longitud=sizeof(int);
	if (this->valor!=NULL)
		delete[] (char*)this->valor;
	this->valor=new int[1];
	*(int *)this->valor=valor;
	return *this;
}

Campo &Campo::operator=(const long &valor)
{
	this->tipo=LONG;
	this->longitud=sizeof(long);
	if (this->valor!=NULL)
		delete[] (char*)this->valor;
	this->valor=new long[1];
	*(long *)this->valor=valor;
	return *this;
}

Campo &Campo::operator=(const float &valor)
{
	this->tipo=FLOAT;
	this->longitud=sizeof(float);
	if (this->valor!=NULL)
		delete[] (char*)this->valor;
	this->valor=new float[1];
	*(float *)this->valor=valor;
	return *this;
}

Campo &Campo::operator=(const double &valor)
{
	this->tipo=DOUBLE;
	this->longitud=sizeof(double);
	if (this->valor!=NULL)
		delete[] (char*)this->valor;
	this->valor=new double[1];
	*(double *)this->valor=valor;
	return *this;
}

bool Campo::operator==(const Campo &campo) const
{
	//if (this->tipo==campo.tipo)
	//{
		switch(this->tipo)
		{
		case CHAR:
			return (strcmp((const char *)this->valor,
				(const char *)campo.valor)==0);
		case INT:
			return (*(int *)this->valor)==(*(int *)campo.valor);
		case LONG:
			return (*(long *)this->valor)==(*(long *)campo.valor);
		case FLOAT:
			return (*(float *)this->valor)==(*(float *)campo.valor);
		case DOUBLE:
			return (*(double *)this->valor)==(*(double *)campo.valor);
		default:
			throw string("Tipo desconocido");
		}
	//}
	//else
		//throw string("No coincide el tipo");
}

bool Campo::operator<(const Campo &campo) const
{
	//if (this->tipo==campo.tipo)
	//{
		switch(this->tipo)
		{
		case CHAR:
			return (strcmp((const char *)this->valor,
				(const char *)campo.valor)<0);
		case INT:
			return (*(int *)this->valor)<(*(int *)campo.valor);
		case LONG:
			return (*(long *)this->valor)<(*(long *)campo.valor);
		case FLOAT:
			return (*(float *)this->valor)<(*(float *)campo.valor);
		case DOUBLE:
			return (*(double *)this->valor)<(*(double *)campo.valor);
		default:
			throw string("Tipo desconocido");
		}
	//}
	//else
		//throw string("No coincide el tipo");
}

void Campo::read(istream &is)
{
	switch(this->tipo)
	{
	case CHAR:
		is.read((char *)this->valor,this->longitud-1);
		break;
	case INT:
		is.read((char *)this->valor,sizeof(int));
		break;
	case LONG:
		is.read((char *)this->valor,sizeof(long));
		break;
	case FLOAT:
		is.read((char *)this->valor,sizeof(float));
		break;
	case DOUBLE:
		is.read((char *)this->valor,sizeof(double));
		break;
	default:
		throw string("Tipo desconocido");
	}
}

void Campo::write(ostream &os) const
{
	switch(this->tipo)
	{
	case CHAR:
		os.write((char *)this->valor,this->longitud-1);
		break;
	case INT:
		os.write((char *)this->valor,sizeof(int));
		break;
	case LONG:
		os.write((char *)this->valor,sizeof(long));
		break;
	case FLOAT:
		os.write((char *)this->valor,sizeof(float));
		break;
	case DOUBLE:
		os.write((char *)this->valor,sizeof(double));
		break;
	default:
		throw string("Tipo desconocido");
	}
}

ostream &operator<<(ostream &os,const Campo &campo)
{
	switch(campo.tipo)
	{
	case Campo::CHAR:
		os << (const char *)campo.valor;
		break;
	case Campo::INT:
		os << (*(int *)campo.valor);
		break;
	case Campo::LONG:
		os << (*(long *)campo.valor);
		break;
	case Campo::FLOAT:
		os << (*(float *)campo.valor);
		break;
	case Campo::DOUBLE:
		os << (*(double *)campo.valor);
		break;
	default:
		throw string("Tipo desconocido");
	}

	return os;
}

istream &operator>>(istream &is,Campo &campo)
{
	switch(campo.tipo)
	{
	case Campo::CHAR:
	{
		string valor;
		is >> valor;
		campo=valor.c_str();
		break;
	}
	case Campo::INT:
		is >> (*(int *)campo.valor);
		break;
	case Campo::LONG:
		is >> (*(long *)campo.valor);
		break;
	case Campo::FLOAT:
		is >> (*(float *)campo.valor);
		break;
	case Campo::DOUBLE:
		is >> (*(double *)campo.valor);
		break;
	default:
		throw string("Tipo desconocido");
	}

	return is;
}
